/**
	License:		Artistic license 2.0 (see license.txt, also available online at http://www.perlfoundation.org/artistic_license_2_0)
	Authors:		OrbitalLab (http://www.orbitallab.ru/moonglide/), 2007-2009

	File:			OpenGL.d
	Description:	main opengl renderer class
	Date:			25.11.2007 by Digited
*/
module moonglide.renderer.opengl.OpenGLRenderer;

import
    moonglide.renderer.opengl.OpenGLHDR_hack;

import
	tango.text.convert.Layout,
	tango.text.convert.Integer,
	tango.text.Util,

	moonglide.util.MoonUtil,
	moonglide.math.MoonMath,
	moonglide.io.LogSystem,
	moonglide.os.IOS,

	moonglide.core.Events,
	moonglide.core.EngObjects,

	moonglide.renderer.IRenderer,

	moonglide.renderer.opengl.GL,
	moonglide.renderer.opengl.OpenGLMatSys,
	moonglide.renderer.opengl.OpenGLRes,
	moonglide.renderer.opengl.OpenGLShaderWork,
	moonglide.renderer.opengl.OpenGLOffscreen,

	moonglide.resources.Management;

const char[] DEF_SCREENSHOT_EXT = ".jpg";

version(Windows)
{
	import
		moonglide.os.win32.Win32OS,
		moonglide.renderer.opengl.platform.OpenGLWin32,
		derelict.opengl.wgl;
}
else version(linux)
{
	import
		moonglide.os.linux.LinuxOS,
		moonglide.renderer.opengl.platform.OpenGLLinux,
		derelict.opengl.glx;
}

const char[] SCREENSHOTS_PATH_FILESYS_DRIVER = "program";

//-----------------------------------------------------------------------
char[] CCharToDChar( char* cch )
{
	if( cch == null )
		return "";

	uint i = 0;
	char[] result;

	while( cch[i] != '\0' )
	{
		i++;
	}

	result = cch[0..i].dup;

	return result;
}
//-----------------------------------------------------------------------

//===============================================================
///	GlGetError - writes opengl errors to console
//===============================================================
bool GetGLError(char[] ch_file, long num_line )
{
    debug
{
	uint err = glGetError();
	bool IsError = false;

	while( err != GL_NO_ERROR )
	{
		IsError = true;
		errorLog( Format( "OpenGL error: {}, caught at <{}> line <{}>",
			CCharToDChar( cast(char*)gluErrorString( err )), ch_file, num_line ));

		err = glGetError();
	}

	return IsError;
}
    return false;
}




//===============================================================
///	OpenGLcaps - works with opengl extensions
//===============================================================
class COpenGLCaps
{
	protected:
		char[][] _extensions;

		void _Detect()
		{
		 	useMultiTex = ARBMultitexture.isEnabled();
			useCubeMap = ARBTextureCubeMap.isEnabled();

			if( useMultiTex )
				glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS_ARB , &maxTextures );

			else maxTextures = 1;

			glGetIntegerv( GL_MAX_TEXTURE_SIZE , &maxTextureSize );
			glGetIntegerv( GL_MAX_LIGHTS , &maxLights );

			useAnisotropy = EXTTextureFilterAnisotropic.isEnabled();

			if( useAnisotropy )
				glGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT , &maxAnisotropyFilter );
			else
				maxAnisotropyFilter = 1;

			attentionLogB("=======caps=============");
			traceLog.info( "Hardware statistics:" );
			traceLog.info( "Videocard vendor: " ~ CCharToDChar(  glGetString( GL_VENDOR ) ) );
			traceLog.info( "Renderer: " ~ CCharToDChar(  glGetString( GL_RENDERER ) ) );
			traceLog.info( "Version: " ~ CCharToDChar(  glGetString( GL_VERSION ) ) );
			traceLog.info( Format( "Max textures: {}", maxTextures ));
			traceLog.info( Format( "Max texture size: {}", maxTextureSize ));
			traceLog.info( Format( "Max lights: ", maxLights ));
			traceLog.info( Format( "Max anisotropy filter: {}", maxAnisotropyFilter ));

			traceLog.info("Extensions: " );

			foreach( extension; tango.text.Util.split( CCharToDChar( glGetString( GL_EXTENSIONS )), " " ))
			{
				if( extension.length == 0 )
					continue;

				traceLog.info( extension );
				_extensions ~= extension;
			}

			traceLog.info( Format( "Extensions count: {}", _extensions.length ));
			attentionLogB("========================");

			traceLog.info( "Multitex enabled: " ~ ( useMultiTex ? "true" : "false" ));
			traceLog.info( "Cubemap enabled: " ~ ( useCubeMap ? "true" : "false" ));

            useVBO = ARBVertexBufferObject.isEnabled();
			traceLog.info( "Vertex buffer object enabled: " ~ ( useVBO ? "true" : "false" ));

			useFBO = EXTFramebufferObject.isEnabled();
			traceLog.info( "Fragment buffer object enabled: " ~ ( useFBO ? "true" : "false" ));

			useARB_vertex_prog = ARBVertexProgram.isEnabled();
			traceLog.info( "ARB vertex shaders enabled: " ~ ( useARB_vertex_prog ? "true" : "false" ));

			if( useARB_vertex_prog )
			{
				glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, &maxENV_VP);

                useARB_frag_prog = ARBFragmentProgram.isEnabled();
                traceLog.info( "ARB pixel shaders enabled: " ~ ( useARB_frag_prog ? "true" : "false" ));

                if( useARB_frag_prog )
                    glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, &maxENV_FP);
			} else useARB_frag_prog = false;

		 	useGLSL = useARB_vertex_prog &&
		 			  useARB_frag_prog &&
		 			  ARBShaderObjects.isEnabled() &&
		 			  ARBShadingLanguage100.isEnabled();


			traceLog.info( "GLSL enabled: " ~ ( useGLSL ? "true" : "false" ));

			attentionLogB("========================");
		}

	public:
		int		maxTextures,
				maxTextureSize,
				maxLights,
				maxAnisotropyFilter,
				maxLightCount,
				maxENV_FP=0,
				maxENV_VP=0;
		bool	useVBO,
				useMultiTex,
				useAnisotropy,
				useARB_frag_prog,
				useARB_vertex_prog,
				useGLSL,
				useFBO,
				useCubeMap;

		bool ErrorChk( char[] fil, long lin )
		{
			debug
				return GetGLError( fil, lin );

			return false;
		}

		bool IsExtension(char[] ext)
		{
			foreach( char[] lin; _extensions )
				if( lin == ext )
					return true;

			return false;
		}

		this()
		{
			_Detect();
		}
}

//===============================================================
///	OpenGLRenderer - main opengl renderer class
//===============================================================
class COpenGLRenderer : public IRenderer
{
	protected:
		this() {}

		bool					_initialized,
								_deinitialized;

		static COpenGLRenderer	_singleton;

		COpenGL_HDR             _hdrHack;
     //   CResTexture tex;
     //   CTextureFilterBlur fil;

     	CResTextureData 		_screenshot_tex;


        bool canUsePostProcess()
        {
            return caps.useGLSL && caps.useFBO;
        }
	public:
		static COpenGLRenderer	opCall()
		{
			if( !_singleton )
				_singleton = new COpenGLRenderer();

			return _singleton;
		}

		~this() { deInitialize(); }

		COpenGLCaps		caps;
		COpenGLShader	shaderMan;

/*
        void initFilter()
        {
            tex = CResTexture( "FilteredTex" );
            tex.data ~= CResTextureData( "data/n.jpg", true );
            fil = new CTextureFilterBlur();
            fil.setInput(tex);
        }

        void deInitFilter()
        {
            delete fil;
        }
*/
	debug
	{
		CEngGeometry debugLightPointPrim;
		CEngGeometry debugGeomPrim;
		CEngGeometry debugCameraPrim;

		void debugInit()
		{
			debugGeomPrim = new CEngGeometry("dbgGeomPrim");
			debugCameraPrim = new CEngGeometry("dbgCamPrim");
			debugLightPointPrim = new CEngGeometry("dbgLigPrim");
			with ( debugLightPointPrim )
			{
			    lodDistance = -1;
                addLod( genMeshLight, 0 );

                material.bools["Lighting"]  = false;
                with( material )
                {
                    vectors4["color"]		= Vector4f(1.0f,1.0f,0.0f,1.0f);
                    vectors4["ambient"]		= Vector4f(1.0f,1.0f,0.0f,1.0f);
                    vectors4["diffuse"]		= Vector4f(1.0f,1.0f,0.0f,1.0f);
                    vectors4["specular"]	= Vector4f(0.0f,0.0f,0.0f,1.0f);
                    vectors4["emission"]	= Vector4f(1.0f,1.0f,0.0f,1.0f);
                }
			}


		}

		void debugRender()
		{
			foreach( inout obj ; xLight )
			{
				debugLightPointPrim.wposition = obj.wposition;
				debugLightPointPrim.worientation = obj.worientation;

				RendGeometry(debugLightPointPrim);
			}
/*
			foreach( inout obj ; xGeom )
			{
				debugGeomPrim.wposition = obj.wposition;
				debugGeomPrim.worientation = obj.worientation;

				RendGeometry(debugGeomPrim.GetAsGeom());
			}

*/
        }
    }
		//===============================================================
		///	Initialize - os window and opengl setup stuff
		//===============================================================
		bool initialize(
			IOS		ios,
			char[]	windowtitle,
			Size2ui	windowsize,
			ubyte	bpp,
			ubyte	refreshFreq,
			bool	fullscreen,
			bool 	vsync,
			bool 	antialiasing,
			bool 	stencil )
		{
			DerelictGL.load();
			DerelictGLU.load();

			traceLog.trace( "OpenGL loaded" );

			bool init;

			version(Windows)
			{
				COpenGLWin32 tmpOSDep = new COpenGLWin32(ios);

				init = tmpOSDep.initialize(
					windowtitle, windowsize, bpp, refreshFreq, fullscreen, vsync, antialiasing, stencil );

				osDependent = tmpOSDep;
			}
			version(linux)
			{
				COpenGLLinux tmpOSDep = new COpenGLLinux(ios);
				init = tmpOSDep.initialize(
					windowtitle, windowsize, bpp, refreshFreq, fullscreen, vsync, antialiasing, stencil );
				osDependent = tmpOSDep;
			}

			fpsCounter			= new CFpsCounter();
			caps				= new COpenGLCaps;
			shaderMan 			= new COpenGLShader(caps);
			resLoader			= new COpenGLRendererRes(caps, shaderMan);
			materialSys			= new COpenGLMatSys(this);


			if( init )
				traceLog.trace( "Renderer's os subsystem init success" );
			else
			{
				errorLog( "Renderer's os subsystem init failure!" );
				return false;
			}

			viewport			= windowsize;
			_initialized		= true;

			glShadeModel( GL_SMOOTH );
			glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );

			glEnable( GL_COLOR_MATERIAL );
//
			glEnable( GL_TEXTURE_2D );
			glEnable( GL_DEPTH_TEST );
			glDepthFunc( GL_LEQUAL );

			glEnable( GL_LIGHT0 );

			glClearColor( 0.0, 0.0, 0.0, 1.0 );
			glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT );

            Vector4f globalAmbient;
            globalAmbient.set(0,0,0,1);

            glLightModelfv(GL_LIGHT_MODEL_AMBIENT, globalAmbient.ptr);

            if( canUsePostProcess() )
            {
                _hdrHack = new COpenGL_HDR (this);
                _hdrHack.init();
            }

            debug debugInit();

			return true;
		}

		//===============================================================
		///	DeInitialize - shutdown & clear stuff
		//===============================================================
		bool deInitialize()
		{
			if( !_initialized || _deinitialized )
				return false;

//            deInitFilter();

            if( canUsePostProcess() )
                delete _hdrHack;

          	_deinitialized = true;
			clearArrays();
			osDependent.deInitialize();

			delete fpsCounter;
			delete resLoader;
			delete materialSys;
			//delete offscreenMan;
			delete shaderMan;
			delete caps;
			delete osDependent;

			return true;
		}

		//===============================================================
		///	RendGeometry - subj
		//===============================================================
		void RendParSystem( CEngParSystem par )
		{
			if ( !par.visible )
				return;

			debug
			{
				glPushMatrix();

				static Matrix44f mat4;
				static Matrix33f mat3tr;

                mat3tr = par.worientation;
				mat4.set( mat3tr );
				mat4.setTranslation(par.wposition);
				glMultMatrixf(mat4.ptr);

				Vector3f lin = par.vector*10;
				glColor3f(1.0f,1.0f,0.0f);
				glBegin(GL_LINES);
					glVertex3f(0,0,0);
					glVertex3fv(lin.ptr);
				glEnd();
				glBegin(GL_POINTS);
					glVertex3fv(lin.ptr);
				glEnd();
				glColor3f(0.5f,0.5f,0.5f);
				glPopMatrix();
			}

			glPointSize(10.0f);

			materialSys.renderGeom( par.getAsGeom );

			//RendGeometry( par.GetAsGeom );
		}


		//===============================================================
		///	RendGeometry - subj
		//===============================================================
		void RendGeometry( CEngGeometry geom )
		{
			curGeom = geom;

			glPushMatrix();

			static Matrix44f mat4;

			mat4.set(geom.worientation);
			mat4.setTranslation(geom.wposition);
			glMultMatrixf(mat4.ptr);

			materialSys.renderGeom( geom );

			glPopMatrix();
		}

		//===============================================================
		///	RendSprite - subj
		//===============================================================
		void RendSprite( CEngSprite spr )
		{

            if ( spr.bools["ScreenAligned"] )
            {
                Vector3f zTracePos ( Vector3f pos, out bool in_vis )
                {
/*                      Matrix44d tmpMV;
                    tmpMV.set(
                            curCam.worientation[0, 0], curCam.worientation[1, 0], curCam.worientation[2, 0], 0.0,
                            curCam.worientation[0, 1], curCam.worientation[1, 1], curCam.worientation[2, 1], 0.0,
                            curCam.worientation[0, 2], curCam.worientation[1, 2], curCam.worientation[2, 2], 0.0,
                            -curCam.wposition.x      ,-curCam.wposition.y       ,-curCam.wposition.z       , 1.0 );

*/
                    double proj[16]=[
                            curCam.projection[0, 0], curCam.projection[1, 0], curCam.projection[2, 0], curCam.projection[3, 0],
                            curCam.projection[0, 1], curCam.projection[1, 1], curCam.projection[2, 1], curCam.projection[3, 1],
                            curCam.projection[0, 2], curCam.projection[1, 2], curCam.projection[2, 2], curCam.projection[3, 2],
                            curCam.projection[0, 3], curCam.projection[1, 3], curCam.projection[2, 3], curCam.projection[3, 3]];
                    double mod[16]=[
                            curCam.worientation[0, 0], curCam.worientation[1, 0], curCam.worientation[2, 0], 0.0,
                            curCam.worientation[0, 1], curCam.worientation[1, 1], curCam.worientation[2, 1], 0.0,
                            curCam.worientation[0, 2], curCam.worientation[1, 2], curCam.worientation[2, 2], 0.0,
                            -curCam.wposition.x      ,-curCam.wposition.y       ,-curCam.wposition.z       , 1.0 ];

                    int view[4] = [0,0,curCam.viewport.x,curCam.viewport.y];

                    glGetDoublev( GL_PROJECTION_MATRIX, &proj[0] );
                    glGetDoublev( GL_MODELVIEW_MATRIX, &mod[0] );
                    glGetIntegerv( GL_VIEWPORT, &view[0] );


                    double outX,outY,outZ;
                    gluProject ( cast(double)pos.x,cast(double)pos.y,cast(double)pos.z,
                                &mod[0], &proj[0], &view[0], &outX, &outY, &outZ );

                    float da;
                    Vector3f xvec = Vector3f(outX,curCam.viewport.y-outY,outZ);

                    if( spr.bools["ZVisiTest"] )
                    {
                        glReadPixels ( cast(int)round(outX), cast(int)round(outY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &da );

                        in_vis=(outX>0)&&(outX<curCam.viewport.x)&&
                               (outY>0)&&(outY<curCam.viewport.y)&&(da > xvec.z);
                    } else in_vis = true;

                    return xvec;
                }

                bool visi = false;
                spr.coord2d = zTracePos(spr.wposition, visi);


                spr.mat.vectors4["color"].w += (visi) ? spr.floats["alphadelta"] * fpsCounter.getLastFrametime : -spr.floats["alphadelta"] * fpsCounter.getLastFrametime;
                clamp( spr.mat.vectors4["color"].w, 0.f, 1.f );

                spr.visible = spr.mat.vectors4["color"].w > 0.f;
            }

            if ( spr.visible && ( !spr.bools["ScreenAligned"] ) )
            {
                materialSys.resBinder.unBindLights();
                materialSys.resBinder.bindMaterial( spr.mat );

                static float halfX, halfY;
                halfX = spr.size.x / 2.f;
                halfY = spr.size.y / 2.f;

                glPushMatrix();

                static Matrix44f mat4;
                static Vector3f zeroVec = { 0.f, 0.f, 0.f };


//                mat4.set( curCam.worientation.inverse );
  //              mat4.multTranslation( spr.wposition );
   //             glMultMatrixf( mat4.ptr );

                static Vector3f _vv1,_vv2,_vv3,_vv4;
                halfX = spr.size.x / 2.f;
                //halfY = spr.size.y / 2.f;
                _vv1 = spr.wposition+curCam.v1*halfX;
                _vv2 = spr.wposition+curCam.v2*halfX;
                _vv3 = spr.wposition+curCam.v3*halfX;
                _vv4 = spr.wposition+curCam.v4*halfX;

                glBegin( GL_QUADS );
                    glTexCoord2f( 0.0, 1.0 );
                    glVertex3fv( _vv1.ptr );

                    glTexCoord2f( 0.0, 0.0 );
                    glVertex3fv( _vv2.ptr );

                    glTexCoord2f( 1.0, 0.0 );
                    glVertex3fv( _vv3.ptr );

                    glTexCoord2f( 1.0, 1.0 );
                    glVertex3fv( _vv4.ptr );
                glEnd();

                glPopMatrix();

                materialSys.resBinder.unBindMaterial( spr.mat );
		    }
        }


		//===============================================================
		///	RendSprite2d - subj
		//===============================================================
		void RendSprite2d( CEngSprite spr )
		{

            if ( ( spr.visible ) && ( spr.bools["ScreenAligned"] ) )
            {
                materialSys.resBinder.unBindLights();
                materialSys.resBinder.bindMaterial( spr.mat );

                static Vector3f sprVec, sprVec2;
                static Matrix33f sprRot;
                static float an, halfX, halfY;
                halfX = spr.size.x / 2.f;
                halfY = spr.size.y / 2.f;

                if( spr.bools["rot"] )
                {
                    an = (curCam.v1.x+curCam.v3.y)*spr.floats["rotSpeed"];

                    getMat2f( an, sprRot );

                    glBegin( GL_QUADS );
                        glTexCoord2f( 1.0, 1.0 );
                        sprVec.set( halfX, halfY, 0 );
                        sprVec2 = sprRot * sprVec;
                        glVertex2f( spr.coord2d.x+sprVec2.x, spr.coord2d.y+sprVec2.y );

                        glTexCoord2f( 1.0, 0.0 );
                        sprVec.set( halfX, -halfY, 0 );
                        sprVec2 = sprRot * sprVec;
                        glVertex2f( spr.coord2d.x+sprVec2.x, spr.coord2d.y+sprVec2.y );

                        glTexCoord2f( 0.0, 0.0 );
                        sprVec.set( -halfX, -halfY, 0 );
                        sprVec2 = sprRot * sprVec;
                        glVertex2f( spr.coord2d.x+sprVec2.x, spr.coord2d.y+sprVec2.y );

                        glTexCoord2f( 0.0, 1.0 );
                        sprVec.set( -halfX, halfY, 0 );
                        sprVec2 = sprRot * sprVec;
                        glVertex2f( spr.coord2d.x+sprVec2.x, spr.coord2d.y+sprVec2.y );
                    glEnd();
                } else
                {
                    glBegin( GL_QUADS );
                        glTexCoord2f( 1.0, 1.0 );
                        sprVec.set( halfX, halfY, 0 );
                        glVertex2f( spr.coord2d.x+sprVec.x, spr.coord2d.y+sprVec.y );

                        glTexCoord2f( 1.0, 0.0 );
                        sprVec.set( halfX, -halfY, 0 );
                        glVertex2f( spr.coord2d.x+sprVec.x, spr.coord2d.y+sprVec.y );

                        glTexCoord2f( 0.0, 0.0 );
                        sprVec.set( -halfX, -halfY, 0 );
                        glVertex2f( spr.coord2d.x+sprVec.x, spr.coord2d.y+sprVec.y );

                        glTexCoord2f( 0.0, 1.0 );
                        sprVec.set( -halfX, halfY, 0 );
                        glVertex2f( spr.coord2d.x+sprVec.x, spr.coord2d.y+sprVec.y );
                    glEnd();
                }
                materialSys.resBinder.unBindMaterial( spr.mat );
            }
		}

		//================================================================
		/// Starts scene by clearing surface, buffers and object arrays
		//================================================================
		void startScene()
		{
			//glClearColor( 0.0, 0.0, 0.0, 1.0 );
			glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT );
		}


		//===============================================================
		///	Renders object arrays (xGeom, xLight, xSpr)
		//===============================================================
		void render()
		{
			debug
			{
                    glDisable(GL_LIGHTING);
                    glDisable(GL_TEXTURE_2D);
                    glEnable(GL_COLOR_MATERIAL);
                    debugRender;

                    glBegin(GL_LINES);
					glColor3f(0,1,0);
					glVertex3i(0,-100,0);
					glVertex3i(0,100,0);

                    glVertex3i(0,100,0);
					glVertex3i(10,90,0);
                    glVertex3i(0,100,0);
					glVertex3i(-10,90,0);

					glColor3f(1,0,0);
					glVertex3i(-100,0,0);
					glVertex3i(100,0,0);

                    glVertex3i(100,0,0);
					glVertex3i(90,10,0);
                    glVertex3i(100,0,0);
					glVertex3i(90,-10,0);

					glColor3f(0,0,1);
					glVertex3i(0,0,-100);
					glVertex3i(0,0,100);

                    glVertex3i(0,0,100);
					glVertex3i(0,10,90);
                    glVertex3i(0,0,100);
					glVertex3i(0,-10,90);
				glEnd();
			}

            for (int i = 0; i < xGeom.length; i++ )
				xGeom[i].sortIndx = xGeom[i].rendPriority * 1000000 + xGeom[i].disToCam;

			xGeom.sort;

            if( canUsePostProcess() )
                _hdrHack.beginRender();

            for (int i = 0; i < xGeom.length; i++ )
				RendGeometry(  xGeom[i] );
            for (int i = 0; i < xParSys.length; i++ )
				RendParSystem(  xParSys[i] );
            for (int i = 0; i < xSpr.length; i++ )
				RendSprite(  xSpr[i] );

            if( canUsePostProcess() )
            {

                if( _hdrHack.useTempDistortion )
                {
                    materialSys.termalPass = true;
                    _hdrHack.beginTempRender();

                    for (int i = 0; i < xGeom.length; i++ )
                        RendGeometry(  xGeom[i] );
                    for (int i = 0; i < xParSys.length; i++ )
                        RendParSystem(  xParSys[i] );

                    _hdrHack.endTempRender();
                    materialSys.termalPass = false;
                }

                _hdrHack.beginOnlyHdrRender();
       /*         materialSys.onlyHdrPass = true; /// very slow part. why?

                foreach( inout geo; xGeom )
                    RendGeometry(  geo );
                foreach( inout par; xParSys )
                    RendParSystem( par );
                foreach( inout spr; xSpr )
                    RendSprite( spr );

                materialSys.onlyHdrPass = false;
    */
            }
                init2DEngine();
                materialSys.depthPass = false;

            if( canUsePostProcess() ) {
                _hdrHack.endRender();
                _hdrHack.renderBloom();
            }

            foreach( inout spr; xSpr )
				RendSprite2d( spr );

			osDependent.printPlatformText(
				"FPS: " ~ floatToStr(fpsCounter.fps),
				Vector2f( 10, 10 ),
				Color4f( 1, 0, 0, 1 ));

            deInit2DEngine();

            glViewport( 0, 0, xviewport.x, xviewport.y );

            debug caps.ErrorChk(__FILE__,__LINE__);
			clearArrays();
		}

//================================================================
/// presents the rendered frame by swapping buffers.
/// NOTE: swapping buffers waits until all rendering is done
//================================================================
		bool presentFrame()
		{
			debug caps.ErrorChk(__FILE__,__LINE__);
			return osDependent.presentFrame();
		}

//================================================================
/// set projection parameters from cam for current rendering pass
//================================================================
		void setActiveCamera( CEngCamera cam )
		{
            curCam = cam;

            if( curCam.changed ) // projection matrix has been changed
            {
    // FIXME (Jura#1#): if cameras > 1 then may be buGs. Why? I dont now what camera must to resize it viewport

               // debugLog.trace("set cam x:"~floatToStr(cam.viewport().x) ~ " y:"~ floatToStr(cam.viewport().y) );
                glViewport( 0, 0, cam.viewport().x, cam.viewport().y );

                if(_hdrHack)
                    _hdrHack.setScreenSize( cam.viewport().x, cam.viewport().y );

                glMatrixMode( GL_PROJECTION );
                glLoadMatrixf( cam.projection.ptr );

                glMatrixMode( GL_MODELVIEW );
            }

			static Matrix44f tmp;
			tmp.set( cam.worientation );
			tmp.multTranslation( -cam.wposition );
			glLoadMatrixf( tmp.ptr );
		}

//===============================================================
/// subj
//===============================================================
		bool saveScreenshot()
		{
			static bool
				screenshots_init,
				screenshots_disabled;

			if( screenshots_disabled )
				return false;

			static long lastIndex = 0;
			lastIndex++;

			static VfsFolder programFolder;

			if( !screenshots_init )	// do once per application run for optimization
			{
				screenshots_init = true;

				if( "program" in CMoonFile.drivers )
					programFolder = CMoonFile.drivers[ SCREENSHOTS_PATH_FILESYS_DRIVER ];
				else
				{
					errorLog( "COpenGLRenderer: could not get screenshots filesystem driver" );
					screenshots_disabled = true;
					return false;
				}

				if( programFolder.writable )
				{
					if( !programFolder.folder( "screenshots" ).exists )
						programFolder.folder( "screenshots" ).create;
					else
					{
						// here we must find max existing screenshot file number and change lastIndex
						auto scan = new FileScan;
						scan( programFolder.toString ~ "/screenshots", DEF_SCREENSHOT_EXT, false );

						char[][] splitted;
						long screenshotNumber;

						foreach( file; scan.files )
						{
							splitted = tango.text.Util.split( file.name, "screenshot" );

							if( splitted.length > 0 )
							{
								screenshotNumber = 0;

								try
								{
									// Exception can be thrown while converting.
									// We don't want the whole engine to crash just because of it, right?
									screenshotNumber = toLong( splitted[1] );
								}
								catch( Exception e )
								{
									errorLog( "exception caught while converting screenshot file number to long int. Reason: "
										~ e.msg );
								}

								if( screenshotNumber >= lastIndex )
									lastIndex = screenshotNumber + 1; // set saving number to next after existing
							}
						}
					}
				}
				else
				{
					errorLog( "COpenGLRenderer: access denied to write to screenshots filesystem driver" );
					screenshots_disabled = true;
					return false;
				}

				_screenshot_tex = CResTextureData( "screenshot_tmp", false );
				_screenshot_tex.setProcedural( false );
				_screenshot_tex.CreateTempTex( viewport.x, viewport.y, IL_RGB, IL_UNSIGNED_BYTE );
			}

			static uint format;
			format = GL_RGB;

			static int vx, vy;
			vx = viewport.x;
			vy = viewport.y;

			if( _screenshot_tex.width != vx || _screenshot_tex.height != vy )
				_screenshot_tex.Resize( vx, vy );

			glReadPixels( 0, 0, viewport.x, viewport.y, format, GL_UNSIGNED_BYTE, _screenshot_tex.GetData );

			static char[] savename;
			savename = Format( "screenshots/screenshot{}{}", lastIndex, DEF_SCREENSHOT_EXT );

			return _screenshot_tex.save( savename );
		}


		Vector4f getActiveWorGeomPos()
		{
			if( curGeom !is null )
				return Vector4f( curGeom.wposition );

			return Vector4f.zero;
		}


		Vector4f getActiveWorLightPos()
		{
			if(( materialSys.resBinder.curLights.length > 0 ) &&
				( materialSys.resBinder.curLights[0] !is null ))
				return Vector4f( materialSys.resBinder.curLights[0].wposition );

			return Vector4f.zero;
		}


		Vector4f getActiveWorCameraPos()
		{
			if( curCam !is null )
				return Vector4f( curCam.position );

			return Vector4f.zero;
		}


		Vector4f getActiveObjLigthPos()
		{
			int num = 0;

			if( curGeom is null )
				return Vector4f.zero;

			if( materialSys.resBinder.curLights[num] is null )
				return Vector4f.zero;


			Matrix33f mat3;
			mat3 = curGeom.worientation;
			mat3.transpose();

  			Vector3f vec;
  			vec = materialSys.resBinder.curLights[num].wposition - curGeom.wposition;
			vec = mat3 * vec;
			vec.normalize();

 			Vector4f res;
			res.set(vec.x,vec.y,vec.z,1);
			return res;
  		}


		Vector4f getActiveObjCameraPos()
		{
			if( curGeom is null )
				return Vector4f.zero;

			if( curCam is null )
				return Vector4f.zero;

			Matrix33f mat3;
			mat3 = curGeom.worientation;
			mat3.transpose();

  			Vector3f vec;
			vec = curCam.wposition - curGeom.wposition;
  			vec = mat3 * vec;
			vec.normalize();

			Vector4f res;
			res.set(vec.x,vec.y,vec.z,1);
			return res;
		}


		Vector4f getActiveModelwGeomPos()
		{
			if( curGeom !is null )
				return Vector4f( curGeom.wposition );

  			Vector3f vec;
  			vec = curCam.orientation * curGeom.wposition;

 			Vector4f res;
			res.set(vec.x,vec.y,vec.z,1);
			return res;
		}


		Vector4f getActiveModelwLigthPos()
		{
			int num = 0;

			if( materialSys.resBinder.curLights[num] is null )
				return Vector4.zero;

			Matrix33f mat3;
			mat3 = curCam.orientation;
			mat3.transpose();

  			Vector3f vec;
  			vec = mat3 * materialSys.resBinder.curLights[num].wposition;

 			Vector4f res;
			res.set(vec.x,vec.y,vec.z,1);
			return res;
		}


		debug void DbgReport()
		{
			debugLogY( "----------------" );
			debugLogY( "COpenGLRenderer. begin of report" );
			debugLogY( "File: <" ~ __FILE__ ~ ">" );
			debugLogY( "----------------" );

			debugLogY("COpenGLRenderer. end of report");
			debugLogY("----------------");
		}



		/// must be in GUI
        void fullScreenQuad()
        {
            glDisable(GL_LIGHTING);
            glDepthMask(GL_FALSE);
            glBegin(GL_QUADS);
            glColor4f(1.f,1.f,1.f,1.f);
            glTexCoord2i(0,1);
            glVertex2i(0,0);
            glTexCoord2i(0,0);
            glVertex2i(0,  viewport.y);

            glTexCoord2i(1,0);
            glVertex2i(viewport.x,viewport.y);
            glTexCoord2i(1,1);
            glVertex2i(viewport.x,0);
            glEnd();
            glDepthMask(GL_TRUE);
        }

        void init2DEngine()
        {
            glDepthFunc(GL_ALWAYS);
            glMatrixMode(GL_PROJECTION);
            glPushMatrix();

            Matrix44f	mat = Matrix44f.ortho( 0.0, cast(float)viewport.x, cast(float)viewport.y, 0.0 );

            glLoadMatrixf(mat.ptr);

            glMatrixMode(GL_MODELVIEW);
            glPushMatrix();
            glLoadIdentity();
            glDisable(GL_LIGHTING);
        }

        void deInit2DEngine()
        {
            glEnable(GL_LIGHTING);
            glPopMatrix();
            glMatrixMode(GL_PROJECTION);
            glPopMatrix();
            glMatrixMode(GL_MODELVIEW);
            glDepthFunc(GL_LESS);
        }
}
